package com.ndp.fb.mq.service.schedulerservice.adpublish;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ndp.fb.enums.publish.PublishResultType;
import com.ndp.fb.enums.publish.PublishStatus;
import com.ndp.fb.exception.ErrorCode;
import com.ndp.fb.fbagent.service.AdSetFBService;
import com.ndp.fb.model.fbagent.param.APIPriority;
import com.ndp.fb.model.fbagent.param.ApiCallBackTarget;
import com.ndp.fb.model.fbagent.param.adset.AdSetParam;
import com.ndp.fb.model.fbagent.result.ReDownloadObject;
import com.ndp.fb.model.fbagent.result.adset.AdSetReDownload;
import com.ndp.fb.model.fbagent.result.adset.FbAdSet;
import com.ndp.fb.mongo.api.PublishCacheMongoService;
import com.ndp.fb.rdb.api.*;
import com.ndp.fb.rdb.model.AdSet;
import com.ndp.fb.rdb.model.BaseAdLevel;
import com.ndp.fb.rdb.model.PublishBatch;
import com.ndp.fb.redis.model.AdCampaignGroupLevel;
import com.ndp.fb.redis.model.AdSetLevel;
import com.ndp.fb.util.ThreadPool;
import com.restfb.DefaultJsonMapper;
import com.restfb.JsonMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by Jacky on 2015/3/12.
 */
@Component
public class PublishAdSetService extends PublishBase {
    public static final String AD_SET = "adSet";
    private static Logger logger = LoggerFactory.getLogger(PublishAdSetService.class);
    @Reference
    private AdSetFBService adSetFBService;
    @Autowired
    private PublishAdGroupService publishAdGroupService;
    @Reference
    private AdSetRdbService adSetRdbService;
    @Reference
    private PublishResultRecordRdbService publishResultRecordRdbService;
    @Reference
    private PublishResultDetailRdbService publishResultDetailRdbService;
    @Reference
    private FbUserRdbService fbUserRdbService;
    @Reference
    private PublishBatchRdbService publishBatchRdbService;


    @Autowired
    private PublishCacheMongoServiceFailover publishCacheMongoService;


    @Override
    public void publish(Long batchId, String adCampaignGroupBatchIndex, BaseAdLevel baseAdLevel) {
        logger.info("PublishAdSetJob->publishAdSets-开始创建AdCampaignGroup：" + adCampaignGroupBatchIndex + "下的所有AdSets.");
        AdCampaignGroupLevel adCampaignGroupLevel=(AdCampaignGroupLevel)baseAdLevel;
        PublishBatch publishBatch = publishBatchRdbService.findById(batchId);
        if (checkBatch(batchId, publishBatch)) return;
        //每次都取最新的 accessToken
        String accessToken = fbUserRdbService.getTokenByUserId(publishBatch.getUserId());

        //3、判断adCampaignGroup发布成功与否
        boolean campaignSucced=adCampaignGroupLevel !=null&&adCampaignGroupLevel.getStatus() == PublishStatus.SUCCEED.getCode();
        for (String adSetBatchIndex : adCampaignGroupLevel.getAdSetBatchIndexs()) {
            AdSetLevel adSetLevel=null;
            try {
                 adSetLevel =JSON.parseObject(publishCacheMongoService.findCacheBlockByTypeAndKey(
                        batchId,PublishCacheMongoService.ADSET,adSetBatchIndex),AdSetLevel.class);
                adSetLevel.setCampaignGroupId(adCampaignGroupLevel.getCampaignGroupId());
                if(!campaignSucced){
                    savePublishResult(adSetLevel,PublishResultType.adSetFail, ErrorCode.ERROR_20008,AD_SET);
                    publishAdGroupService.publish(batchId, adSetBatchIndex, adSetLevel);
                }else{
                    if(adSetLevel.getStatus()==PublishStatus.SUCCEED.getCode()&&publishBatch.getRetryCnt()>0){
                        //如果 adset level 已经成功，重试的过程中并不知道该adset 对应的 ad是否都成功了，所以还要走一遍流程
                        publishAdGroupService.publish(batchId, adSetBatchIndex, adSetLevel);
                    }else if(adSetLevel.getStatus()==PublishStatus.WAIT_PUPLISH.getCode()||adSetLevel.getStatus()==PublishStatus.FAIL.getCode()){
                        //3-1-1.构造FB参数（需要取到FB返回的adCampaignGroupId）
                        adSetLevel.setAccessToken(accessToken);
                        AdSetParam param = generateFbParam(adSetLevel,adSetBatchIndex);
                        //3-1-2.发送请求
                        adSetFBService.createAdSet(param);
                        publishCacheMongoService.updateStatus(adSetLevel.getPublishBatchId(),adSetLevel.getAdSetKey(), PublishStatus.PUBLISHING.getCode(),PublishCacheMongoService.ADSET);
                    }
                }
                logger.info("PublishAdSetJob->publishAdSets-结束创建AdCampaignGroup：" + adCampaignGroupBatchIndex + "下的所有AdSets.");
            }catch (Exception e){
                logger.error("PublishAdSetJob->publishAdSets-创建失败AdCampaignGroup：" + adCampaignGroupBatchIndex + "下的所有AdSets.");
                publishCacheMongoService.updateStatus(adSetLevel.getPublishBatchId(),adSetLevel.getAdSetKey(),PublishStatus.FAIL.getCode(),PublishCacheMongoService.ADSET);
            }
        }
    }


    /**
     * 组件fb请求参数
     * @param adSetLevel
     * @return
     */
    private AdSetParam generateFbParam(AdSetLevel adSetLevel,String adSetBatchIndex) {
        AdSetParam param = new AdSetParam();
        param.setAccessToken(adSetLevel.getAccessToken());
        param.setFbAccountId(Long.valueOf(adSetLevel.getActAccountId().split("act_")[1]));

        param.setName(adSetLevel.getName());
        param.setStatus(adSetLevel.getCampaignStatus());
        param.setDailyBudget(adSetLevel.getDailyBudget());
        param.setLifetimeBudget(adSetLevel.getLifetimeBudget());
        param.setStartTime(adSetLevel.getStartTimeString());
        param.setEndTime(adSetLevel.getEndTimeString());
        param.setCampaignId(adSetLevel.getCampaignGroupId());
        param.setTargeting(adSetLevel.getTargeting());
        param.setPromotedObject(adSetLevel.getPromotedObject());
        param.setApiPriorityCode(APIPriority.CreateAdSet.getCode());
        param.setResultClass(ReDownloadObject.class);
        param.setCallBack(true);
        param.setRedownload(true);
        param.setOptimizationGoal(adSetLevel.getOptimizationGoal());
        param.setBillingEvent(adSetLevel.getBillingEvent());
        if (null != adSetLevel.getIsAutobid() && adSetLevel.getIsAutobid()) {
            param.setIsAutobid(true);
        } else {
            param.setBidAmount(adSetLevel.getBidAmount());
        }
        Map<String, String> equipment = new HashMap<>();
        param.setApiCallbackTargetType(ApiCallBackTarget.AdPublish.AdSet.getTargetType());
        equipment.put("publishBatchId", adSetLevel.getPublishBatchId().toString());
        equipment.put("adSetBatchIndex", adSetBatchIndex);
        equipment.put("campaignGroupId", String.valueOf(adSetLevel.getCampaignGroupId()));

        param.setEquipment(equipment);
        return param;
    }

    @Override
    public void callback(Map<String, String> equipment, Object callBackObject) {
        ThreadPool.execute(() -> {
            logger.info("PublishAdSetJob->publishAdSetCallBack->start->AdSet创建成功回调开始!");
            PublishStatus publishStatus=null;
            String adSetBatchIndex=null;
            Long publishBatchId=null;
            AdSetLevel adSetLevel=null;
            try {
                logger.debug("PublishAdSetJob->publishAdSetCallBack->equipment:" + JSON.toJSONString(equipment));
                logger.debug("PublishAdSetJob->publishAdSetCallBack->response:" + JSON.toJSONString(callBackObject));
                publishBatchId = Long.valueOf(equipment.get("publishBatchId"));
                //2.通过equipment中的adSetBatchIndex获取批次下的AdSetLevel
                adSetBatchIndex = equipment.get("adSetBatchIndex");
                logger.info("PublishAdSetJob->publishAdSetCallBack->AdSet创建成功回调开始!adSetBatchIndex>>" + adSetBatchIndex);
                adSetLevel =JSON.parseObject(publishCacheMongoService.findCacheBlockByTypeAndKey(
                        publishBatchId,PublishCacheMongoService.ADSET, adSetBatchIndex),AdSetLevel.class);
                adSetLevel.setCampaignGroupId(Long.valueOf(equipment.get("campaignGroupId")));
                publishStatus= callbackHandler(adSetLevel, callBackObject);
            } catch (Exception e) {
                publishStatus=PublishStatus.FAIL;
                logger.error("->publishAdSetCallBack->广告组发布失败", e);
            }finally {
                try {
                    if(publishStatus==PublishStatus.SUCCEED)
                        publishCacheMongoService.fillFbId(publishBatchId,adSetBatchIndex,String.valueOf(adSetLevel.getAdSetId()),PublishCacheMongoService.ADSET);
                    publishCacheMongoService.updateStatus(publishBatchId,adSetBatchIndex,publishStatus.getCode(),PublishCacheMongoService.ADSET);
                    publishAdGroupService.publish(Long.valueOf(publishBatchId), adSetBatchIndex, adSetLevel);
                    logger.info("PublishAdSetJob->publishAdSetCallBack->AdSet创建成功回调结束!adSetBatchIndex>>" + adSetBatchIndex);
                }catch (Exception ex){
                    logger.error("->publishAdSetCallBack finally error->广告组发布失败", ex);
                }
            }
        });
    }

    @Override
    PublishResultDetailRdbService getPublishResultDetailRdbService() {
        return this.publishResultDetailRdbService;
    }

    @Override
    PublishResultRecordRdbService getPublishResultRecordRdbService() {
        return this.publishResultRecordRdbService;
    }

    private PublishStatus callbackHandler(AdSetLevel adSetLevel, Object callBackObject) {
        PublishResultType publishResultType;
        String exception = null;
        PublishStatus publishStatus;
        ReDownloadObject reDownLoad = null;
        Long adSetId = null;
        if (callBackObject instanceof ReDownloadObject) {
            reDownLoad = (ReDownloadObject) callBackObject;
            adSetId = reDownLoad.getId();
            publishStatus = PublishStatus.SUCCEED;
            publishResultType = PublishResultType.adSetSuccess;
        } else {
            exception = callBackObject.toString();
            publishStatus = PublishStatus.FAIL;
            publishResultType = PublishResultType.adSetFail;
            logger.error("PublishAdSetJob-> publishAdSetCallBack->fb fail:" + exception);
        }
        adSetLevel.setStatus(publishStatus.getCode());
        if (PublishStatus.SUCCEED.getCode() == adSetLevel.getStatus()) {
            //4-2 如果成功则回填 FB返回的 adSetId
            adSetLevel.setAdSetId(adSetId);
            //4-3、如果成功AdSet入库到ad_set表
            AdSetReDownload o = JSON.parseObject(reDownLoad.getData(), AdSetReDownload.class);
            String adSetJson = null;
            if (o.getCampaigns() != null) {
                //兼容2.4
                adSetJson = o.getCampaigns().get(adSetId);
            } else if (o.getAdsets() != null) {
                adSetJson = o.getAdsets().get(adSetId);
            }
            updateAdSetBusiness(adSetJson, adSetLevel);
        }
        savePublishResult(adSetLevel, publishResultType, exception,AD_SET);
        return  publishStatus;
    }



    private void updateAdSetBusiness(String adSetJson, AdSetLevel adSetLevel) {
        JsonMapper jsonMapper = new DefaultJsonMapper();
        FbAdSet fbAdSet = jsonMapper.toJavaObject(adSetJson, FbAdSet.class);
        //// fixme: 2015/12/9  因为CPC暂时适配2.4
        JSONObject jsonObject = JSON.parseObject(adSetJson);
        if (fbAdSet.getCampaignGroupId() == null) {
            fbAdSet.setCampaignGroupId(Long.valueOf(jsonObject.get("campaign_group_id").toString()));
        }
        if (fbAdSet.getCampaignStatus() == null) {
            fbAdSet.setCampaignStatus(jsonObject.get("campaign_status").toString());
        }
        // 适配2.4 end
        adSetLevel.setAdSetId(fbAdSet.getId()); //todo debug
        if (adSetLevel.getActAccountId() == null) {
            adSetLevel.setActAccountId("act_"+fbAdSet.getAccountId());
        }
        AdSet adSet = generateAdSet(adSetLevel);
        BeanUtils.copyProperties(fbAdSet, adSet);
        //处理特殊字段
        adSet.setRtbFlag(fbAdSet.getRtbFlag() ? 1 : 0);
        adSetRdbService.save(adSet);
    }


    private AdSet generateAdSet(AdSetLevel adSetLevel) {
        AdSet adSet = new AdSet();
        adSet.setAdSetId(adSetLevel.getAdSetId());
        adSet.setFbAccountId(Long.valueOf(adSetLevel.getActAccountId().split("act_")[1]));
        adSet.setCampaignGroupId(adSetLevel.getCampaignGroupId());
        adSet.setPublishBatchId(adSetLevel.getPublishBatchId());
        adSet.setFbConnectionObjectId(adSetLevel.getFbConnectionObjectId());
        adSet.setProductConfId(adSetLevel.getProductConfigurationId());
        adSet.setName(adSetLevel.getName());
        adSet.setCampaignStatus(adSetLevel.getCampaignStatus());
        adSet.setDailyBudget(adSetLevel.getDailyBudget());
        adSet.setLifetimeBudget(adSetLevel.getLifetimeBudget());
        adSet.setStartTime(adSetLevel.getStartTime());
        adSet.setEndTime(adSetLevel.getEndTime());
        adSet.setStartUtcTime(adSetLevel.getStartUTCTime());
        adSet.setEndUtcTime(adSetLevel.getEndUTCTime());
        adSet.setUpdatedTime(adSetLevel.getUpdateTime());
        adSet.setCreatedTime(adSetLevel.getCreateTime());
        adSet.setCreatedUtcTime(adSetLevel.getCreateUTCTime());
        adSet.setUpdatedUtcTime(adSetLevel.getUpdateUTCTime());
        if (adSetLevel.getPromotedObject() != null) {
            adSet.setPromotedObject(adSetLevel.getPromotedObject().toString());

        }
        return adSet;
    }
}
